<!DOCTYPE html>
<html>
<head>
  <!-- USE CDN, PUBLIC BUILD -->
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@compound-finance/compound-js@latest/dist/browser/compound.min.js"></script>

  <!-- USE LOCAL BUILD (be sure `npm install`) -->
  <!-- <script type="text/javascript" src="../../dist/browser/compound.min.js"></script> -->

  <link rel="stylesheet"
      href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.1.2/build/styles/default.min.css">
  <script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.1.2/build/highlight.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.2/languages/javascript.min.js"></script>
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.1.2/styles/night-owl.min.css">
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/picnic" />
  <style type="text/css">
    p { margin: 0px }
    pre { padding: 0px }
    .red { color: #e60000; }
    .container {
      margin: 0px 25px;
      width: 800px;
    }
    .spinner { /* credit https://codepen.io/mandelid/pen/vwKoe */
      display: inline-block;
      width: 20px;
      height: 20px;
      border: 3px solid rgba(0,0,0,.3);
      border-radius: 50%;
      border-top-color: #000;
      animation: spin 1s ease-in-out infinite;
      -webkit-animation: spin 1s ease-in-out infinite;
    }
    @keyframes spin { to { -webkit-transform: rotate(360deg); } }
    @-webkit-keyframes spin { to { -webkit-transform: rotate(360deg); } }
  </style>
  <title>Compound.js Examples</title>
</head>
<body>
  <div class="container">
    <h1>Compound.js Examples [Alpha]</h1>
    <p>
      Code examples for client interaction with the <a href="https://compound.finance/">Compound</a> protocol via Compound.js.
    </p>

    &nbsp;
    
    <p>
        Run <a href="https://www.trufflesuite.com/ganache">Ganache</a> with a <b>fork of main net</b> on your local machine and set MetaMask to <b>Localhost 8545</b> to test writing transactions.
    </p>

    &nbsp;

    <p class="red">
      <b>
        DO NOT use this web page with Ethereum main net or with your funded Ethereum keys / wallet!
      </b>
    </p>

    &nbsp;

    <div class="item">
      <h2>Ethereum Read (JSON RPC eth_call)</h2>
      <p>Generic Ethereum blockchain read with JSON RPC. This example fetches the supply rate per block for cETH using the <b>read</b> method.</p>
      <button id="eth-call-button">Get Supply Rate Per Block</button>
      <br />
      <div id="eth-call-content"></div>
      <pre><code class="js">// mainnet
const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';

(async function() {

  const srpb = await Compound.eth.read(
    cEthAddress,
    'function supplyRatePerBlock() returns (uint256)',
    // [], // [optional] parameters
    // {}  // [optional] call options, provider, network, plus Ethers.js "overrides"
  );

  console.log('cETH market supply rate per block:', srpb.toString());

})().catch(console.error);</code></pre>
    </div>

    &nbsp;

    <div class="item">
      <h2>Ethereum Send Transaction (JSON RPC eth_sendTransaction)</h2>
      <p>Create and send an Ethereum transaction with JSON RPC. This button's transaction transfers your Ether to the Compound protocol using the <b>trx</b> method.</p>
      <button id="eth-sendTransaction-button">Supply 1 ETH</button>
      <br />
      <div id="eth-sendTransaction-content"></div>
      <pre><code class="js">const oneEthInWei = '1000000000000000000';
const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
const provider = window.ethereum;

(async function() {
  console.log('Supplying ETH to the Compound Protocol...');

  // Mint some cETH by supplying ETH to the Compound Protocol
  const trx = await Compound.eth.trx(
    cEthAddress,
    'function mint() payable',
    [],
    {
      provider,
      value: oneEthInWei
    }
  );

  // const result = await trx.wait(1); // JSON object of trx info, once mined

  console.log('Ethers.js transaction object', trx);
})().catch(console.error);</code></pre>
    </div>

    &nbsp;

    <div class="item">
      <h2>Supply</h2>
      <p>This button's transaction transfers your Ether to the Compound protocol using the <b>supply</b> method.</p>
      <button id="supply-button">Supply 1 ETH</button>
      <br />
      <div id="supply-content"></div>
      <pre><code class="js">const compound = new Compound(window.ethereum);

// Ethers.js overrides are an optional 3rd parameter for `supply`
// const trxOptions = { gasLimit: 250000, mantissa: false };

(async function() {

  console.log('Supplying ETH to the Compound protocol...');
  const trx = await compound.supply(Compound.ETH, 1);
  console.log('Ethers.js transaction object', trx);

})().catch(console.error);</code></pre>
    </div>

    &nbsp;

    <div class="item">
      <h2>Redeem</h2>
      <p>This button's transaction redeems your Ether from the Compound protocol using the <b>redeem</b> method.</p>
      <button id="redeem-button">Redeem 1 ETH</button>
      <br />
      <div id="redeem-content"></div>
      <pre><code class="js">const compound = new Compound(window.ethereum);

(async function() {

  console.log('Redeeming ETH...');
  const trx = await compound.redeem(Compound.ETH, 1); // also accepts cToken args
  console.log('Ethers.js transaction object', trx);

})().catch(console.error);
</code></pre>
    </div>

    &nbsp;

    <div class="item">
      <h2>Enter Markets</h2>
      <p>This button's transaction enables supplied assets to be used as collateral using the <b>enterMarkets</b> method. Note that there is a corresponding <b>exitMarket</b> method for exiting a single market.</p>
      <button id="enter-market-button">Enter ETH Market</button>
      <br />
      <div id="enter-market-content"></div>
      <pre><code class="js">const compound = new Compound(window.ethereum);

(async function() {

  console.log('Entering ETH market (use as collateral)...');
  const trx = await compound.enterMarkets(Compound.ETH); // also accepts []

  console.log('Ethers.js transaction object', trx);

  // Exit a market (string argument of only 1 market at a time)
  // const trx = await compound.exitMarket(Compound.ETH);

})().catch(console.error);</code></pre>
    </div>

    &nbsp;

    <div class="item">
      <h2>Borrow</h2>
      <p>This button's transaction borrows 32 Dai, against collateral, using the <b>borrow</b> method. Remember to supply a supported asset as collateral and enter that market prior to borrowing.</p>
      <button id="borrow-button">Borrow 32 Dai</button>
      <br />
      <div id="borrow-content"></div>
      <pre><code class="js">const compound = new Compound(window.ethereum);

(async function() {

  const daiScaledUp = '32000000000000000000';
  const trxOptions = { mantissa: true };

  console.log('Borrowing 32 Dai...');
  const trx = await compound.borrow(Compound.DAI, daiScaledUp, trxOptions);

  console.log('Ethers.js transaction object', trx);

})().catch(console.error);</code></pre>
    </div>

    &nbsp;

    <div class="item">
      <h2>Repay Borrow</h2>
      <p>This button's transaction repays 32 borrowed Dai using the <b>repayBorrow</b> method. This won't work unless you have an open borrow.</p>
      <button id="repay-borrow-button">Repay 32 Dai</button>
      <br />
      <div id="repay-borrow-content"></div>
      <pre><code class="js">const compound = new Compound(window.ethereum);

(async function() {

  console.log('Repaying Dai borrow...');
  const address = null; // set this to any address to repayBorrowBehalf
  const trx = await compound.repayBorrow(Compound.DAI, 32, address);

  console.log('Ethers.js transaction object', trx);

})().catch(console.error);</code></pre>
    </div>

    &nbsp;

    <div class="item">
      <h2>Get Price</h2>
      <p>Fetches the BAT price in USDC using the <b>getPrice</b> method.</p>
      <button id="get-price-button">Get BAT Price</button>
      <br />
      <div id="get-price-content"></div>
      <pre><code class="js">(async function() {

  // Accepts 2 args, cTokens or underlyings. Second arg defaults to USDC.
  const price = await compound.getPrice(Compound.BAT);
  console.log('BAT in USDC', price);

})().catch(console.error);</code></pre>
    </div>

    &nbsp;

    <div class="item">
      <h2>Get Address</h2>
      <p>Fetches a relevant contract address using <b>getAddress</b> method.</p>
      <button id="get-address-button">Get cETH Address</button>
      <br />
      <div id="get-address-content"></div>
      <pre><code class="js">// Accepts cTokens or underlyings. Second arg defaults to main net.
const cEthAddressRopsten = Compound.util.getAddress(Compound.cETH, 'ropsten');</code></pre>
    </div>

    &nbsp;

  </div>

</body>
<script type="text/javascript">

const ethCallButton = document.getElementById('eth-call-button');
const ethCallContent = document.getElementById('eth-call-content');
const ethSendTransactionButton = document.getElementById('eth-sendTransaction-button');
const ethSendTransactionContent = document.getElementById('eth-sendTransaction-content');
const supplyButton = document.getElementById('supply-button');
const supplyContent = document.getElementById('supply-content');
const redeemButton = document.getElementById('redeem-button');
const redeemContent = document.getElementById('redeem-content');
const enterMarketButton = document.getElementById('enter-market-button');
const enterMarketContent = document.getElementById('enter-market-content');
const borrowButton = document.getElementById('borrow-button');
const borrowContent = document.getElementById('borrow-content');
const repayBorrowButton = document.getElementById('repay-borrow-button');
const repayBorrowContent = document.getElementById('repay-borrow-content');
const getPriceButton = document.getElementById('get-price-button');
const getPriceContent = document.getElementById('get-price-content');
const getAddressButton = document.getElementById('get-address-button');
const getAddressContent = document.getElementById('get-address-content');

const spin = (element) => {
  element.innerText = '';
  element.classList.add('spinner');
};

const unspin = (element) => element.classList.remove('spinner');

const ethCallExample = () => {
  spin(ethCallContent);

  // mainnet
  const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';

  (async function() {

    const srpb = await Compound.eth.read(
      cEthAddress,
      'function supplyRatePerBlock() returns (uint256)',
      // [], // [optional] parameters
      // {}  // [optional] call options, provider, network, plus Ethers.js "overrides"
    );

    ethCallContent.innerText = srpb.toString();
    console.log('cETH market supply rate per block:', srpb.toString());

  })().catch((err) => {
    ethCallContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(ethCallContent) });
};


/** 
 * Run ganache-cli in another command line window before running this script. Be
 *     sure to fork mainnet.

ganache-cli \
  -f https://mainnet.infura.io/v3/_YOUR_INFURA_ID_ \
  -m "clutch captain shoe salt awake harvest setup primary inmate ugly among become" \
  -i 1

 */
const ethSendTransactionExample = () => {
  spin(ethSendTransactionContent);

  const oneEthInWei = '1000000000000000000';
  const cEthAddress = '0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5';
  const provider = window.ethereum;

  (async function() {
    console.log('Supplying ETH to the Compound Protocol...');

    // Mint some cETH by supplying ETH to the Compound Protocol
    const trx = await Compound.eth.trx(
      cEthAddress,
      'function mint() payable',
      [],
      {
        provider,
        value: oneEthInWei
      }
    );

    // const result = await trx.wait(1); // JSON object of trx info, once mined

    ethSendTransactionContent.innerText = 'Success, see the developer console for the Ethers.js transaction object.';
    console.log('Ethers.js transaction object', trx);
  })().catch((err) => {
    ethSendTransactionContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(ethSendTransactionContent) });

}


/** 
 * Run ganache-cli in another command line window before running this script. Be
 *     sure to fork mainnet.

ganache-cli \
  -f https://mainnet.infura.io/v3/_YOUR_INFURA_ID_ \
  -m "clutch captain shoe salt awake harvest setup primary inmate ugly among become" \
  -i 1

 */
const supplyExample = () => {
  spin(supplyContent);

  const compound = new Compound(window.ethereum);

  // Ethers.js overrides are an optional 3rd parameter for `supply`
  // const trxOptions = { gasLimit: 250000, mantissa: false };

  (async function() {

    console.log('Supplying ETH to the Compound protocol...');
    const trx = await compound.supply(Compound.ETH, 1);
    supplyContent.innerText = 'Success, see the developer console for the Ethers.js transaction object.';
    console.log('Ethers.js transaction object', trx);

  })().catch((err) => {
    supplyContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(supplyContent) });
};

/** 
 * Run ganache-cli in another command line window before running this script. Be
 *     sure to fork mainnet.

ganache-cli \
  -f https://mainnet.infura.io/v3/_YOUR_INFURA_ID_ \
  -m "clutch captain shoe salt awake harvest setup primary inmate ugly among become" \
  -i 1

 */
const redeemExample = () => {
  spin(redeemContent);

  const compound = new Compound(window.ethereum);

  (async function() {

    console.log('Redeeming ETH...');
    const trx = await compound.redeem(Compound.ETH, 1); // also accepts cToken args
    console.log('Ethers.js transaction object', trx);
    redeemContent.innerText = 'Success, see the developer console for the Ethers.js transaction object.';

  })().catch((err) => {
    redeemContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(redeemContent) });
};


/**
 * Run ganache-cli in another command line window before running this script. Be
 *     sure to fork mainnet.

ganache-cli \
  -f https://mainnet.infura.io/v3/_YOUR_INFURA_ID_ \
  -m "clutch captain shoe salt awake harvest setup primary inmate ugly among become" \
  -i 1

 */
const enterMarketExample = () => {
  spin(enterMarketContent);

  const compound = new Compound(window.ethereum);

  (async function() {

    console.log('Entering ETH market (use as collateral)...');
    const trx = await compound.enterMarkets(Compound.ETH); // also accepts []

    console.log('Ethers.js transaction object', trx);

    // Exit a market (string parameter of only 1 market at a time)
    // const trx = await compound.exitMarket(Compound.ETH);

    enterMarketContent.innerText = 'Success, see the developer console for the Ethers.js transaction object.';

  })().catch((err) => {
    enterMarketContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(enterMarketContent) });
};


/** 
 * Run ganache-cli in another command line window before running this script. Be
 *     sure to fork mainnet.

ganache-cli \
  -f https://mainnet.infura.io/v3/_YOUR_INFURA_ID_ \
  -m "clutch captain shoe salt awake harvest setup primary inmate ugly among become" \
  -i 1

 */
const borrowExample = () => {
  spin(borrowContent);

  const compound = new Compound(window.ethereum);

  (async function() {

    const daiScaledUp = '32000000000000000000';
    const trxOptions = { mantissa: true };

    console.log('Borrowing 32 Dai...');
    const trx = await compound.borrow(Compound.DAI, daiScaledUp, trxOptions);

    borrowContent.innerText = 'Success, see the developer console for the Ethers.js transaction object.';
    console.log('Ethers.js transaction object', trx);

  })().catch((err) => {
    borrowContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(borrowContent) });
};


/** 
 * Run ganache-cli in another command line window before running this script. Be
 *     sure to fork mainnet.

ganache-cli \
  -f https://mainnet.infura.io/v3/_YOUR_INFURA_ID_ \
  -m "clutch captain shoe salt awake harvest setup primary inmate ugly among become" \
  -i 1

 */
const repayBorrowExample = () => {
  spin(repayBorrowContent);

  const compound = new Compound(window.ethereum);

  (async function() {

    console.log('Repaying Dai borrow...');
    const address = null; // set this to any address to repayBorrowBehalf
    const trx = await compound.repayBorrow(Compound.DAI, 32, address);

    repayBorrowContent.innerText = 'Success, see the developer console for the Ethers.js transaction object.';
    console.log('Ethers.js transaction object', trx);

  })().catch((err) => {
    repayBorrowContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(repayBorrowContent) });
};


// get a price using the open price feed
const getPriceExample = () => {
  spin(getPriceContent);

  const compound = new Compound();

  (async function() {

    // Accepts 2 args, cTokens or underlyings. Second arg defaults to USDC.
    const price = await compound.getPrice(Compound.BAT);
    console.log('BAT in USDC', price);

    getPriceContent.innerText = price.toString();

  })().catch((err) => {
    getPriceContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(getPriceContent) });
};


// get a relevant address of a supported protocol contract
const getAddressExample = () => {
  spin(getAddressContent);

  const compound = new Compound();

  (async function() {

    // Accepts cTokens or underlyings. Second arg defaults to main net.
    const cEthAddressRopsten = Compound.util.getAddress(Compound.cETH, 'ropsten');

    getAddressContent.innerText = cEthAddressRopsten.toString();

  })().catch((err) => {
    getAddressContent.innerText = 'Error occured; See developer console for details.';
    console.error(err);
  }).finally(() => { unspin(getAddressContent) });
};

window.addEventListener('load', (event) => {
  // syntax highlight code snippets
  document.querySelectorAll('pre code').forEach((block) => {
    hljs.highlightBlock(block);
  });

  window.ethereum.enable().then(() => {

    ethCallButton.onclick = ethCallExample;
    ethSendTransactionButton.onclick = ethSendTransactionExample;
    supplyButton.onclick = supplyExample;
    redeemButton.onclick = redeemExample;
    enterMarketButton.onclick = enterMarketExample;
    borrowButton.onclick = borrowExample;
    repayBorrowButton.onclick = repayBorrowExample;
    getPriceButton.onclick = getPriceExample;
    getAddressButton.onclick = getAddressExample;

  });
});

</script>
</html>